﻿/*********************************************************
 * 开发人员：小兵
 * 创建时间：2012-8-30 14:37:22
 * 描述说明：
 * 
 * 更改历史：
 * 
 * *******************************************************/
namespace Mozlite.Components
{
    using System;
    using System.Web;
    using System.Data;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IO;
    using Mozlite.Data;

    /// <summary>
    /// 文件管理类。
    /// </summary>
    public static class FileStorages
    {
        #region path
        /// <summary>
        /// 获取用户文件物理路径。
        /// </summary>
        /// <param name="args">路径参数。</param>
        /// <returns>返回用户文件物理路径。</returns>
        public static string GetUserFilePath(params object[] args)
        {
            string file = null;
            if(args != null)
                file = args.Join("\\");
            file = Cores.GetPath("App_Data\\_userfiles", file);
            Cores.CreateDirectory(Path.GetDirectoryName(file));
            return file;
        }

        /// <summary>
        /// 获取用户图片文件缩略图物理路径。
        /// </summary>
        /// <param name="args">路径参数。</param>
        /// <returns>返回用户图片文件缩略图物理路径。</returns>
        public static string GetUserFileImageThumbnailPath(params object[] args)
        {
            return GetUserFilePath("thumbnail", args.Join("\\"));
        }
        #endregion

        #region helper
        private static string WhereUserID(int userID)
        {
            if (FileStorageSettings.Current.EnableCrossFileSystem)
                return "1=1";
            return "UserID = " + userID;
        }

        private static void RefreshFolders(int userID)
        {
            Caches.Remove(GetCacheKey(userID));
        }

        private static string GetCacheKey(int userID) {
            if (FileStorageSettings.Current.EnableCrossFileSystem)
                return "UserFolders::ALL";
            return "UserFolders::" +  userID;
        }
        #endregion

        /// <summary>
        /// 文件夹。
        /// </summary>
        /// <param name="userID">用户ID。</param>
        /// <returns>返回用户文件夹实例。</returns>
        public static Dictionary<int, UserFolder> GetUserFolders(int userID)
        {
            var cacheKey = GetCacheKey(userID);
            return Caches.Get<Dictionary<int, UserFolder>>(cacheKey, () =>
            {
                var folders = Database.Fetch<UserFolder>("SELECT * FROM moz_Users_Folders WHERE {0};".FormatWith(WhereUserID(userID)))
                    .ToDictionary<UserFolder, int>(folder => folder.FolderID);
                folders.Add(0, new UserFolder { UserID = userID, FolderName = Globals.ToLocal("UserFolder_Root") });
                Caches.Insert(cacheKey, folders);
                return folders;
            });
        }

        /// <summary>
        /// 文件夹。
        /// </summary>
        /// <param name="userID">用户ID。</param>
        /// <returns>返回用户文件夹实例。</returns>
        public static Dictionary<int, List<UserFolder>> GetUserFoldersByParentID(int userID)
        {
            var cacheKey = "UserFoldersByParentID::" + (FileStorageSettings.Current.EnableCrossFileSystem ? "ALL" : userID.ToString());
            return Caches.Get<Dictionary<int, List<UserFolder>>>(cacheKey, () =>
            {
                var folders = new Dictionary<int, List<UserFolder>>();
                using (var reader = Database.ExecuteReader("SELECT * FROM moz_Users_Folders WHERE {0}".FormatWith(WhereUserID(userID))))
                {
                    while (reader.Read())
                    {
                        var folder = reader.As<UserFolder>();
                        if (!folders.ContainsKey(folder.ParentID))
                            folders.Add(folder.ParentID, new List<UserFolder>());
                        folders[folder.ParentID].Add(folder);
                    }
                    reader.Close();
                }
                Caches.Insert(cacheKey, folders, new System.Web.Caching.CacheDependency(null, new string[] { GetCacheKey(userID) }));
                return folders;
            });
        }

        /// <summary>
        /// 文件夹。
        /// </summary>
        /// <param name="userID">用户ID。</param>
        /// <param name="folderID">文件夹ID。</param>
        /// <returns>返回用户文件夹实例。</returns>
        public static List<UserFile> GetUserFiles(int userID, int folderID)
        {
            return Database.Fetch<UserFile>("SELECT u.*, f.ContentType, f.ContentLength FROM moz_Users_Files u INNER JOIN moz_FileStorages f ON u.StorageID = f.StorageID WHERE {0} AND FolderID = {1}".FormatWith(WhereUserID(userID), folderID));
        }

        /// <summary>
        /// 判断文件夹名称是存在。
        /// </summary>
        /// <param name="userID">用户ID。</param>
        /// <param name="folderID">文件夹ID。</param>
        /// <param name="fileName">文件名称。</param>
        /// <param name="parentID">父文件夹ID。</param>
        /// <returns>返回执行结果。</returns>
        public static bool IsFolderNameExisted(int userID, int folderID, string folderName, int parentID)
        {
            return Database.ExecuteScalar<bool>("SELECT True FROM moz_Users_Folders WHERE {0} AND ParentID = @ParentID AND FolderName = @FolderName AND FolderID <> @FolderID".FormatWith(WhereUserID(userID)), new
            {
                ParentID = parentID,
                FolderName = folderName,
                FolderID = folderID
            });
        }

        /// <summary>
        /// 判断文件名称是否存在。
        /// </summary>
        /// <param name="userID">用户ID。</param>
        /// <param name="folderID">文件夹ID。</param>
        /// <param name="fileName">文件名称。</param>
        /// <param name="fileID">文件ID。</param>
        /// <returns>返回执行结果。</returns>
        public static bool IsFileNameExisted(int userID, int folderID, string fileName, int? fileID = null)
        {
            if(fileID == null)
                return Database.ExecuteScalar<bool>("SELECT 1 FROM moz_Users_Files WHERE {0} AND FolderID = @FolderID AND FileName = @FileName;".FormatWith(WhereUserID(userID)), new { 
                    FolderID = folderID,
                    FileName = fileName
                });
            return Database.ExecuteScalar<bool>("SELECT 1 FROM moz_Users_Files WHERE {0} AND FolderID = @FolderID AND FileName = @FileName AND FileID <> @FileID".FormatWith(WhereUserID(userID)), new
            {
                FolderID = folderID,
                FileName = fileName,
                FileID = fileID
            });
        }

        /// <summary>
        /// 获取存储物理文件信息实例。
        /// </summary>
        /// <param name="storageID">存储文件ID。</param>
        /// <returns>返回存储文件信息实例。</returns>
        public static StorageFile GetStorageFile(Guid storageID) {
            return Database.ExecuteReader<StorageFile>("SELECT * FROM moz_FileStorages WHERE StorageID = @StorageID;", new { StorageID = storageID });
        }

        /// <summary>
        /// 通过用户文件ID获取文件实例。
        /// </summary>
        /// <param name="fileID">文件ID。</param>
        /// <returns>返回用户文件实例。</returns>
        public static UserFile GetFile(int fileID)
        {
            return Database.ExecuteScalar<UserFile>("SELECT u.*, f.ContentType, f.ContentLength FROM moz_Users_Files u INNER JOIN moz_FileStorages f ON u.StorageID = f.StorageID WHERE u.FileID = " + fileID);
        }
        /// <summary>
        /// 当前用户的文件夹根目录。
        /// </summary>
        public static UserFolder Root {
            get {
                return new UserFolder { UserID = AppContext.Current.UserID, FolderName = Globals.ToLocal("UserFolder_Root") };
            }
        }
        /// <summary>
        /// 修改名称。
        /// </summary>
        /// <param name="isFolder">是否为文件夹改名。</param>
        /// <param name="id">唯一ID。</param>
        /// <param name="userID">当前用户ID。</param>
        /// <param name="name">修改后的名称。</param>
        /// <param name="extension">扩展名，修改文件时候使用。</param>
        /// <returns>返回修改结果。</returns>
        public static bool ChangeName(bool isFolder, int id, int userID, string name, string extension = null)
        {
            var isSaved = false;
            if(isFolder)
                isSaved = Database.ExecuteNonQuery("UPDATE moz_Users_Folders SET FolderName = @FolderName WHERE {0} AND FolderID = @FolderID".FormatWith(WhereUserID(userID)), new {
                    FolderName = name,
                    FolderID = id
                });
            else
                isSaved = Database.ExecuteNonQuery("UPDATE moz_Users_Files SET FileName = @FileName, Extension = @Extension WHERE {0} AND FileID = @FileID".FormatWith(WhereUserID(userID)), new
                {
                    FileName = name,
                    Extension = extension,
                    FileID = id
                });
            if(isSaved)
                RefreshFolders(userID);
            return isSaved;
        }
        /// <summary>
        /// 新建文件夹。
        /// </summary>
        /// <param name="name">文件夹名称。</param>
        /// <param name="parentID">文件夹父文件夹ID。</param>
        /// <param name="userID">用户ID。</param>
        /// <returns>返回新建结果。</returns>
        public static bool CreateFolder(string name, int parentID, int userID) {
            if (Database.ExecuteNonQuery("INSERT INTO moz_Users_Folders(FolderName, ParentID, CreateDate, LastUpdateDate, UserID, ContentLength)VALUES(FolderName, ParentID, GETDATE(), GETDATE(), UserID, 0);", new { 
                FolderName = name,
                ParentID = parentID,
                UserID = userID
            }))
            {
                RefreshFolders(userID);
                return true;
            }
            return false;
        }
        /// <summary>
        /// 删除文件夹，包含其中的文件。
        /// </summary>
        /// <param name="folderID">文件夹ID。</param>
        /// <param name="userID">用户ID。</param>
        /// <returns>返回执行结果。</returns>
        public static bool DeleteFolder(int folderID, int userID)
        {
            return Database.BeginTransaction<bool>(transaction =>
            {
                string folderIDs = null;
                var folder = GetFolder(folderID, userID);
                if (folder != null && folder.HasChildren)
                {
                    folder.Recurse<UserFolder>(f =>
                    {
                        if (folderIDs != null)
                            folderIDs += ",";
                        folderIDs += f.FolderID;
                    });
                }
                else
                    folderIDs = folderID.ToString();
                transaction.ExecuteNonQuery("DELETE FROM moz_Users_Files WHERE FolderID IN({1}) AND {0}".FormatWith(WhereUserID(userID), folderIDs));
                transaction.ExecuteNonQuery("DELETE FROM moz_Users_Folders WHERE {0} AND FolderID IN({1});".FormatWith(WhereUserID(userID), folderIDs));
                RefreshFolders(userID);
                return true;
            });
        }
        /// <summary>
        /// 获取当前文件夹。
        /// </summary>
        /// <param name="folderID">文件夹ID。</param>
        /// <param name="userID">用户ID。</param>
        /// <returns>返回文件夹实例对象。</returns>
        public static UserFolder GetFolder(int folderID, int userID) {
            UserFolder folder;
            GetUserFolders(userID).TryGetValue(folderID, out folder);
            return folder;
        }
        /// <summary>
        /// 删除文件。
        /// </summary>
        /// <param name="fileID">文件唯一ID。</param>
        /// <param name="userID">用户ID。</param>
        /// <returns>返回执行结果。</returns>
        public static bool DeleteFile(int fileID, int userID)
        {
            if (Database.ExecuteNonQuery("DELETE FROM moz_Users_Files WHERE {0} AND FileID = @FileID;".FormatWith(WhereUserID(userID)), new { FileID = fileID }))
            {
                RefreshFolders(userID);
                return true;
            }
            return false;
        }
        /// <summary>
        /// 移动文件或文件夹。
        /// </summary>
        /// <param name="isFolder">是否为文件夹。</param>
        /// <param name="id">选中的ID。</param>
        /// <param name="targetID">目标文件夹ID。</param>
        /// <param name="userID">用户ID。</param>
        /// <returns>返回移动结果。</returns>
        public static bool Move(bool isFolder, int id, int targetID, int userID) {
            bool isMoved = false;
            if(isFolder)
                isMoved = Database.ExecuteNonQuery("UPDATE moz_Users_Folders SET ParentID = @ParentID WHERE {0} AND FolderID = @FolderID".FormatWith(WhereUserID(userID)), new { ParentID = targetID, FolderID = id });
            else
                isMoved = Database.ExecuteNonQuery("UPDATE moz_Users_Files SET FolderID = @FolderID WHERE {0} AND FileID = @FileID".FormatWith(WhereUserID(userID)), new { FolderID = targetID, FileID = id });
            if(isMoved)
            {
                RefreshFolders(userID);
            }
            return isMoved;
        }
        /// <summary>
        /// 将子文件和子文件列表转换为JSON需要的对象列表。
        /// </summary>
        /// <param name="parent">当前文件夹实例对象。</param>
        /// <returns>返回JSON对象列表。</returns>
        public static IEnumerable<object> ToJsonObject(this UserFolder parent) {
            IEnumerable<object> json = null;
            if(!parent.HasChildren)
                json = parent.Children.ConvertAll(folder => new
                {
                    ID = folder.FolderID,
                    FolderID = folder.ParentID,
                    Name = folder.FolderName,
                    CreateDate = folder.CreateDate.ToString(),
                    Ext = "",
                    LastUpdateDate = folder.LastUpdateDate.ToString(),
                    Size = folder.ContentLength == 0 ? "" : folder.ContentLength.ToSizeString(),
                    IsFolder = true,
                    Url = ""
                });
            var files = parent.Files;
            if(files!=null&&files.Count>0)
            {
                var list = files.ConvertAll(file => new
                {
                    ID = file.FileID,
                    FolderID = file.FolderID,
                    Name = file.FileName,
                    CreateDate = file.CreateDate.ToString(),
                    Ext = file.Extension,
                    LastUpdateDate = file.LastUpdateDate.ToString(),
                    Size = file.ContentLength == 0 ? "" : file.ContentLength.ToSizeString(),
                    IsFolder = false,
                    Url = file.Url
                });
                if(json == null)
                    json = list;
                else
                    json.Union(list);
            }
            return json;
        }
    }
}
